Add support for BNGL models - #501
Conversation
Add a BnglModel loader (a peer of PySBModel/SbmlModel) so that a `language: bngl` PEtab problem loads and validates via petablint / Problem.from_yaml at the model level. See PEtab-dev/PEtab#436. - petab/v1/models/bngl_model.py: BnglModel backed by a small, dependency-free BNGL block reader (parse_bngl). Introspection only; is_valid shells out to `BNG2.pl --check` when a BNG backend is locatable and falls back to True otherwise (mirroring how the SBML loader always validates because libsbml is always present). - Register `bngl` in known_model_types and add a branch to model_factory; v2 picks it up via the existing re-exports (+ a v2 shim module). - tests/v1/test_model_bngl.py and a minimal BNGL fixture: ABC unit tests plus a full Problem.from_yaml validation oracle covering the model-cross checks.
Re-sync with PyBNF's sibling reader (pybnf/petab/_bngl.py, ADR-0026 / lanl/PyBNF#437): - Block aliases: `begin molecules` / `begin species` / `begin rules` now open the same blocks as `molecule types` / `seed species` / `reaction rules` (_block_lines consults a per-canonical-name alias table), per the BNGL grammar reference (BioNetGen Perl2/; BNG_vscode_extension docs/bngl-grammar.md). - Seed-species `$` clamp: `SeedSpeciesDefn = ["$"], Species, ...` -- the `$` fixed-concentration marker is stripped so `$counter() 10` enumerates the state variable `counter()`, keeping is_state_variable correct under the clamp. Adds grammar-hardening tests (alias parsing, `$`-clamp stripping, no cross-block shadowing, the is_state_variable seam) that double as the drift anchor against PyBNF's reader. ruff check + format clean; 19 passed. Refs: PEtab-dev/PEtab#436.
A trailing `\` (BNGL line continuation) splits one logical declaration across physical lines. The block scanner processed physical lines, so a continued parameter/function/observable was truncated at the `\` (e.g. `k = \` read as the value `\`). Add _logical_lines() mirroring BNG2.pl's readFile (Perl2/BNGModel.pm): strip the comment first, then while a line ends with `\` drop it and concatenate the next comment-stripped physical line directly (no space, so `1e\`+`3` -> `1e3`). Surfaced by the bng_parity corpus (895 community BNGL models): 252 use line continuation, incl. inside enumerated blocks (functions, observables, parameters, seed species). Adds continuation + backslash-in-comment tests; kept in sync with PyBNF's sibling reader (pybnf/petab/_bngl.py). ruff clean, 21 passed. Refs: PEtab-dev/PEtab#436.
BNGL declarations may carry a leading line label (LineLabel = {Digit}, WS |
Name, ":", [WS]): a legacy .net-style numeric index (`1 L0 1`) or a named label
(`CD14: CD14(...)`). The reader took the label as the entity -- the index as a
parameter name, the label as the seed species. Add _strip_line_label() and apply
it in the parameter and seed-species extractors (a valid BNGL identifier starts
with a letter, so a leading digit-run is unambiguously an index; a compartment
prefix carries `@`, so a bare `Name:` is unambiguously a label).
Surfaced by a writeModel-based differential over the bng_parity corpus (895
community models): 4 models disagreed with BNG2.pl's canonical parse (indexed
params/seed, labeled seed); after this fix, 0 -- parameters/observables/
functions/molecule-types/compartments all match BNG2.pl across the corpus.
Kept in sync with PyBNF's sibling reader. ruff clean, 24 passed.
Refs: PEtab-dev/PEtab#436.
Asserts parse_bngl enumerates the same model entities BNG2.pl does, over 21 curated public community BNGL models (RuleHub, BNGL-Models) under tests/v1/bngl_corpus/. BNG2.pl's answers are cached in golden.json -- the entity name sets it emits from `writeModel` (its canonical parse, no network generation) -- so the test needs NO BNG2.pl and runs anywhere; it compares the reader against the frozen oracle. Seed species are compared by molecule composition to absorb BNG2.pl's pattern canonicalization (t vs t(), component reordering, @compartment prefix vs suffix). The models exercise every hardened reader path: line continuations, indexed and labeled declarations, block aliases, the $ clamp, compartmental BNGL, energy patterns, states/bonds, component reordering, bare-molecule seed species. The golden is regenerated deliberately (needs BNG2.pl) via `python tests/v1/test_bngl_corpus.py` and reviewed as a diff. Mirrors PyBNF's live-BNG2.pl gate (lanl/PyBNF); validated there over the full 895-model bng_parity corpus (894/894 BNG2.pl-accepted models agree). ruff clean; 21 passed without BNG2.pl. Refs: PEtab-dev/PEtab#436.
…ies`)
The grammar doc lists `molecules` (for `molecule types`) and `rules` (for
`reaction rules`) as block aliases, but BNG2.pl 2.9.3 -- the reference this
reader targets -- REJECTS both ("Could not process block type 'molecules' /
'rules'"). Honoring them let the reader enumerate entities from a block BNG2.pl
refuses, i.e. accept models the reference rejects. Restrict _BLOCK_ALIASES to
`species` (for `seed species`), which BNG2.pl accepts and in fact emits as its
own canonical seed-species spelling. Verified empirically against BNG2.pl 2.9.3.
The corpus gate is unchanged (no fixture uses the dropped aliases; golden
regenerates identically). ruff clean; 24 passed. Refs: PEtab-dev/PEtab#436.
353c172 to
6276d1d
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #501 +/- ##
==========================================
+ Coverage 75.12% 75.40% +0.28%
==========================================
Files 64 66 +2
Lines 7157 7324 +167
Branches 1265 1283 +18
==========================================
+ Hits 5377 5523 +146
- Misses 1290 1307 +17
- Partials 490 494 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Hi Bill, thanks for this contribution. Great to see that there is PEtab-support in PyBNF. I'd be happy to add BNGL support to this library. It will take me some time to have a closer look. Since this adds quite a bit of code - is there maybe a dedicated repository from which we can source the test models on demand? Would it make sense to create a lightweight independent Python BNGL parser package that can be reused elsewhere? |
|
Closing this. #508 superseded it and merged on 14 August, and it carries the |
Adds BNGL as a PEtab model language next to SBML and PySB, so a problem
declaring
language: bnglloads and validates instead of stopping atmodel-load in
petablint.BnglModelreads BNGL with a pure-Python parser that pulls the entitiesvalidation cares about (parameters, species, observables, functions) straight
from the model text. So loading and validating a BNGL problem needs no external
tools and adds no runtime dependency.
is_valid()will additionally runBNG2.pl --checkif that executable is on thePATH, for a stricter check, andreturns
Trueif it isn't — so BNG2.pl is used when available but neverrequired. Both v1 and v2 are covered.
PyBNF has been dogfooding this reader for a while — it uses it to fit rule-based
models against PEtab problems, and its PEtab tutorials exercise the loader end
to end: https://github.com/lanl/PyBNF/tree/main/examples/tutorial
Addresses PEtab-dev/PEtab#436.